﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Supermodel.Exceptions;
using Supermodel.Reflection;
using Supermodel.Repository;

namespace Supermodel.DomainModel
{
    public abstract class ChildEntity<EntityT, ParentEntityT> : Entity<EntityT> 
        where EntityT : ChildEntity<EntityT, ParentEntityT>, new()
        where ParentEntityT : Entity<ParentEntityT>, new()
    {
        #region ParentId Implementation for Child Entities
        [NotMapped]
        public virtual long? ParentId
        {
            get
            {
                if (GetParentEntity() == null) return null;
                return GetParentEntity().Id; 
            }
            set
            {
                if (GetParentEntity() == null && value == null) return;
                if (GetParentEntity() != null && GetParentEntity().Id == value) return;
                SetParentEntity((value == null) ? null : RepoFactory.Create<ParentEntityT>().GetById((long)value));
            }
        }
        #endregion

        #region abstract properties to be implemented by descendants of ChildEntity
        //[NotMapped] protected abstract ParentEntityT ParentEntity22 { get; set; }
        protected abstract void SetParentEntity(ParentEntityT parent);
        protected abstract ParentEntityT GetParentEntity();
        #endregion

        #region Validation
        override public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var results =  (List<ValidationResult>)base.Validate(validationContext);
            if (ParentId == null) results.Add(new ValidationResult("Supermodel: ParentId is null for a ChildEntity."));
            return results;
        }
        #endregion
    }    

    public abstract class Entity<EntityT> : EntityCore where EntityT : Entity<EntityT>, new()
    {
        #region Methods
        public EntityT Add()
        {
            var originalId = Id;
            try
            {
                AddInternal();
                return (EntityT)this;
            }
            catch (Exception)
            {
                Id = originalId;
                throw;
            }
        }
        public void Delete()
        {
            var originalId = Id;
            try
            {
                DeleteInternal();
            }
            catch (Exception)
            {
                Id = originalId;
                throw;
            }
        } 
        #endregion

        #region Overridable Methods
        virtual protected void AddInternal()
        {
            RepoFactory.Create<EntityT>().Add((EntityT)this);
        }
        virtual protected void DeleteInternal()
        {
            RepoFactory.Create<EntityT>().Delete((EntityT)this);
        } 
        #endregion
    }

    //Do not inherit from this class, use Entity<> and ChildEntity<>
    public abstract class EntityCore : IValidatableObject
    {
        #region Standard Properties
        [Key]
        virtual public long Id { get; set; }

        [NotMapped]
        abstract public string Label { get; }
        #endregion

        #region Validation
        virtual public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            return new List<ValidationResult>();
        } 
        #endregion
    }
}
